package fons.navigator.views.island;

import java.awt.Color;
import java.awt.Font;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.Maps;

import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import fons.model.ontology.Dimension;
import fons.model.ontology.ValueSpace;
import fons.navigator.util.piccolo.PLabel;

public class Island extends PPath {
	private static final long serialVersionUID = 1L;
	
	private final static Font LABEL_FONT = new Font("Arial", Font.PLAIN, 14);
	private final VisualIsland visualIsland;
	private final ValueSpace islandSpace;
	private final Map<ValueSpace, Double> values = Maps.newHashMap();
	private final PLabel label;
	private boolean empty = true;
	
	public Island(final VisualIsland visualIsland, final ValueSpace islandSpace) {
		super(new Ellipse2D.Double(-2, -2, 4, 4));
		
		this.visualIsland = visualIsland;
		this.islandSpace = islandSpace;
		this.setPaint(Color.BLUE);
		this.setOffset(0, 0);
		
		this.label = new PLabel(this.getIslandName());
		this.label.setFont(LABEL_FONT);
		visualIsland.getCamera().addChild(label);
		label.setVisible(false);
		
		this.addInputEventListener(new PBasicInputEventHandler() {
			private boolean wasVisible;
			@Override
			public void mouseEntered(PInputEvent event) {
				moveToFront();
				wasVisible = label.getVisible();
				showLabel();
			}
			
			@Override
			public void mouseExited(PInputEvent event) {
				if (!wasVisible) hideLabel();
			}
		});
	}
	
	public void updateLabel() {
		if (label.getVisible())
			showLabel();
	}
	
	public void showLabel() {
		final PCamera cam = visualIsland.getCamera();
		label.setVisible(true);
		Point2D offset = cam.viewToLocal(Island.this.getOffset());
		offset.setLocation(offset.getX() + 5, offset.getY() + 5);
		label.setOffset(offset);
	}
	
	public void hideLabel() {
		label.setVisible(false);
	}

	public void addValue(ValueSpace vsp, Double mappedValue) {
		if (Double.isNaN(mappedValue)) {
			values.put(vsp, 0.0);
		} else {
			values.put(vsp, mappedValue);
			empty = false;
		}
	}
	
	public void placeAt (double x, double y) {
		this.setOffset(x/visualIsland.getValueScale(), y/visualIsland.getValueScale());
		updateLabel();
	}
	
	public void move (double dx, double dy) {
		this.setOffset(this.getXOffset() + dx/visualIsland.getScale(),
					   this.getYOffset() + dy/visualIsland.getScale());
		updateLabel();
	}

	public double getValueOf (ValueSpace vsp) {
		return values.get(vsp);
	}
	
	public double distance (Island island) {
		final double scale = visualIsland.getValueScale();
		final double dx = scale * (this.getXOffset() - island.getXOffset());
		final double dy = scale * (this.getYOffset() - island.getYOffset());
		
		return Math.sqrt(dx*dx + dy*dy);
	}
	
	public double weightDistance (Island island) {
		double distance = 0;
		for (ValueSpace vsp: visualIsland.getValueSpaces()) {
			final double di = this.values.get(vsp) - island.values.get(vsp);
			distance += di*di;
		}
		return Math.sqrt(distance);
	}
	
	public String getIslandName() {
		StringBuilder name = new StringBuilder();
		boolean first = true;
		for (Dimension dim: islandSpace.getDimensions()) {
			if (!first) name.append(" x ");
			name.append(dim.getName());			
			first = false;
		}
		
		for (Entry<ValueSpace, Double> entry: this.values.entrySet()) {
			name.append(", " + entry.getValue());
		}
		
		return name.toString();
	}

	public double getMass() {
		double m = 0;
		for (Double value: this.values.values()) {
			m += Math.abs(value);
		}
		return m;
	}

	public boolean isEmpty() {
		return empty;
	}

	public ValueSpace getIslandSpace() {
		return this.islandSpace;
	}
}
